package indi.cloud.common.utils;


import indi.cloud.common.config.MinioConfig;
import cn.hutool.core.date.DateUtil;

import cn.hutool.http.HttpStatus;
import io.minio.*;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.MinioException;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Component
@Slf4j
@Service
public class MinioUtil {

    private static final Logger logger = LoggerFactory.getLogger(MinioUtil.class);

    @Autowired
    private MinioConfig prop;

    @Resource
    private MinioClient minioClient;

//    @Autowired
//    private CodeService codeService;

    /**
     * 查看存储bucket是否存在
     *
     * @return boolean
     */
    public Boolean bucketExists(String bucketName) {
        Boolean found;
        try {
            found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return found;
    }

    /**
     * 创建存储bucket
     *
     * @return Boolean
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除存储bucket
     *
     * @return Boolean
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 获取全部bucket
     */
    public List<Bucket> getAllBuckets() {
        try {
            List<Bucket> buckets = minioClient.listBuckets();
            return buckets;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 文件上传
     *
     * @param file 文件
     * @return Boolean
     */
    public String upload(MultipartFile file, String originName) {

        String originalFilename = file.getOriginalFilename();
        if (StringUtils.isBlank(originalFilename)) {
            throw new RuntimeException();
        }
        originName = originName.replaceAll("\\\\", "/");
        int index = originName.lastIndexOf("/");
        String fileName = "admin-" + originName.substring(index + 1, originName.length());
        //String fileName = UUIDUtil.getUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
        String objectName = DateUtil.date().toStringDefaultTimeZone() + "-" + fileName;
        try {
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(prop.getBucketName()).object(objectName)
                    .stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return objectName;
    }

    /**
     * 文件下载
     *
     * @param fileName
     * @return
     */
    public void download(String fileName, HttpServletResponse res) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(prop.getBucketName())
                .object(fileName).build();
        try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
            byte[] buf = new byte[1024];
            int len;
            try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
                while ((len = response.read(buf)) != -1) {
                    os.write(buf, 0, len);
                }
                os.flush();
                byte[] bytes = os.toByteArray();
                res.setCharacterEncoding("utf-8");
                res.addHeader("Content-Disposition", "attachment;fileName=" + new String(fileName.getBytes("gb2312"), "ISO8859-1"));
                try (ServletOutputStream stream = res.getOutputStream()) {
                    stream.write(bytes);
                    stream.flush();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void previewPDF(String fileName, HttpServletResponse res) {
        previewPDF(prop.getBucketName(), fileName, res);
    }

    /**
     * pdf文件预览
     *
     * @param fileName 文件名称
     * @param res      response
     * @return Boolean
     */
    public void previewPDF(String bucketName, String fileName, HttpServletResponse res) {
        if(StringUtils.isBlank(bucketName)){
            bucketName = prop.getBucketName();
        }
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName)
                .object(fileName).build();
        try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
            byte[] buf = new byte[1024];
            int len;
            try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
                while ((len = response.read(buf)) != -1) {
                    os.write(buf, 0, len);
                }
                os.flush();
                byte[] bytes = os.toByteArray();
                String extName = getExtNameFromContentDisposition(fileName);
                switch (extName) {
                    case ".pdf":
                        res.setContentType("application/pdf");
                        break;
                    case ".html":
                        res.setContentType("text/html");
                        break;
                    case ".txt":
                        res.setContentType("text/plain");
                        break;
                }
                res.setCharacterEncoding("utf-8");
                res.getOutputStream().write(bytes);
                res.getOutputStream().flush();
            }
        } catch (ErrorResponseException ex){
            // 文件名不存在
            if(StringUtils.equalsIgnoreCase("The specified key does not exist.", ex.getMessage())
                    || StringUtils.equalsIgnoreCase("The specified bucket does not exist", ex.getMessage())){
                res.reset();
                res.setStatus(HttpStatus.HTTP_NOT_FOUND);
            }
        } catch (Exception e) {
            logger.error("minio Exception", e);
        }
    }


    /**
     * 解析网络请求，获取文件类型
     *
     * @param contentDisposition
     * @return
     */
    public String getExtNameFromContentDisposition(String contentDisposition) {
        if (contentDisposition == null) {
            return "";
        }
        String[] exts = new String[]{"pdf", "txt", "doc", "xls", "docx", "jpg", "bmp", "rar", "xlsx", "jpeg", "png", "wps", "ppt", "shtml", "zip", "html"};
        for (String i : exts) {
            if (contentDisposition.contains(i)) {
                return "." + i;
            }
        }
        return "";
    }

//    public String preview(String fileName){
//        // 查看文件地址
//        GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(prop.getBucketName()).object(fileName).method(Method.GET).build();
//        try {
//            String url = minioClient.getPresignedObjectUrl(build);
//            return url;
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return null;
//    }


    /**
     * 查看文件对象
     *
     * @return 存储bucket内文件对象信息
     */
    public List<Item> listObjects() {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(prop.getBucketName()).build());
        List<Item> items = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                items.add(result.get());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return items;
    }

    /**
     * 删除
     *
     * @param fileName
     * @return
     * @throws Exception
     */
    public boolean remove(String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build());
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    public boolean remove(String bucketName, String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    public List<String> minioFileTraversal(String bucketName){
        List<String> fileList = new ArrayList<>();
        try {
            Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).recursive(true).build());
            for (Result<Item> result : results) {
                Item item = result.get();

                System.out.println("Object Name: " + item.objectName());
                System.out.println("Size: " + item.size() + " bytes");
                System.out.println("Last Modified: " + item.lastModified().toString());
                System.out.println("Is Directory?: " + item.isDir());
                System.out.println("---------------------------");

                if (DateUtils.isDateInRange(Date.from(item.lastModified().toInstant()),DateUtils.getDaysAgo(5,new Date()),DateUtils.getDaysAgo(2,new Date()))){
                    fileList.add(item.objectName());
                }

            }
            return fileList;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}